package com.demo.webflux.spring5core.framework.webmvc.servlet;

import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.demo.webflux.spring5core.framework.annotation.Controller;
import com.demo.webflux.spring5core.framework.annotation.RequestMapping;
import com.demo.webflux.spring5core.framework.context.ApplicationContext;
import com.demo.webflux.spring5core.framework.webmvc.HandlerAdapter;
import com.demo.webflux.spring5core.framework.webmvc.HandlerMapping;
import com.demo.webflux.spring5core.framework.webmvc.ModelAndView;
import com.demo.webflux.spring5core.framework.webmvc.View;
import com.demo.webflux.spring5core.framework.webmvc.ViewResolver;
import com.demo.webflux.spring5core.mock.HttpServletRequest;
import com.demo.webflux.spring5core.mock.HttpServletResponse;
import com.demo.webflux.spring5core.mock.ServletConfig;

public class DispatchServlet {

	private final Logger log = LoggerFactory.getLogger(DispatchServlet.class);
	private final String LOCATION = "contextConfigLocation";

	// 保存url和method的映射关系
	private List<HandlerMapping> handlerMappings = new ArrayList<>();
	private Map<HandlerMapping, HandlerAdapter> handlerAdapters = new HashMap<>();
	private List<ViewResolver> viewResolvers = new ArrayList<>();
	private ApplicationContext context;

	public void init(ServletConfig config) {
		// 初始化ioc容器
		context = new ApplicationContext(config.getInitParamter(LOCATION));
		// 扫描相关的类
		initStrategies(context);
	}

	private void initStrategies(ApplicationContext context2) {
		// 有九种策略
		// 针对每个用户请求都会经过一些处理策略处理，最终才能有结果输出
		// 每种策略可以自定义干预，但是最终的结果都一致
		initMultipartResolver(context); // 文件上传解析，如果请求类型是multipart，将通过MultipartResolver进行文件上传解析
		initLocaleResolver(context); // 本地化解析
		initThemeResolver(context); // 主题解析
		initHandlerMappings(context); // 用来保存controller中配置得到RequestMapping和method的对应关系
		initHandlerAdapters(context); // 通过HandlerAdapter进行多类型的参数动态匹配
		initHandlerExceptionResolvers(context); // 如果执行过程中遇到异常，则交给HandlerExceptionResolver来解析
		initRequestToViewNameTranslator(context); // 直接将请求解析到视图名
		initViewResolvers(context); // 通过viewResolver将逻辑视图解析到具体视图实现
		initFlashMapManager(context); // flash映射管理器
	}

	/**将controller中配置的RequestMapping和method进行一一对应
	 * @param context
	 */
	private void initHandlerMappings(ApplicationContext context) {
		// 首先从容器中获取所有的实例
		String[] beanNames = context.getBeanDefinitionNames();
		try {
			for (String beanName : beanNames) {
				// 到了mvc层，对外提供的方法只有一个getBean方法，返回的对象不是BeanWrapper，怎么办？
				Object controller = context.getBean(beanName);
				Class<?> clazz = controller.getClass();
				if(!clazz.isAnnotationPresent(Controller.class)) {
					continue;
				}

				String baseUrl = "";

				// 获取controller上的RequestMapping
				if(clazz.isAnnotationPresent(RequestMapping.class)) {
					RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
					baseUrl = requestMapping.value();
				}
				// 获取method的url配置
				Method[] methods = clazz.getMethods();
				for (Method method : methods) {
					if(!method.isAnnotationPresent(RequestMapping.class)) {
						continue;
					}
					RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
					String regex = ("/" + baseUrl + requestMapping.value()).replaceAll("\\*", ".*/").replaceAll("/+", "/");
					Pattern pattern = Pattern.compile(regex);
					handlerMappings.add(new HandlerMapping(pattern, controller, method));
					log.info("Mapping: " + regex + " , " + method);
				}
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void initHandlerAdapters(ApplicationContext context2) {
		// 在初始化阶段，我们能做的就是，将这些参数的名字或者类型类型按一定的顺序保存下来
		// 因为后面用反射调用的时候，传的形参是一个数组
		// 可以通过记录这些参数的位置index，逐个从数组中取值，这样就和参数的名称无关了
		for (HandlerMapping handlerMapping : handlerMappings) {
			// 每个方法有一个参数列表，这里保存的就是形参列表
			handlerAdapters.put(handlerMapping, new HandlerAdapter());
		}
	}

	private void initViewResolvers(ApplicationContext context2) {
		// 在页面中输入http://127.0.0.1:8080/first.html
		// 解决页面名字和模板文件关联的问题
		String templateRoot = context.getConfig().getProperty("templateRoot");
		String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();

		File templateRootDir = new File(templateRootPath);

		for (File template : templateRootDir.listFiles()) {
			viewResolvers.add(new ViewResolver(templateRoot));
		}
	}

	public void doGet(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		doPost(req, resp);
	}

	public void doPost(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		doDispatch(req, resp);
	}

	private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
		// 根据用户请求的url来获得一个handler
		HandlerMapping handler = getHandler(req);
		if(handler == null) {
			processDispatchResult(req, resp, new ModelAndView("404"));
			return;
		}

		HandlerAdapter ha = getHandlerAdapter(handler);

		// 这一步只是调用方法，得到返回值
		ModelAndView mv = ha.handle(req, resp, handler);

		// 这一步才是真正的输出
		processDispatchResult(req, resp, mv);
	}

	private HandlerMapping getHandler(HttpServletRequest req) {
		if(handlerMappings.isEmpty()) {
			return null;
		}
		String url = req.getRequestURI();
		String contextPath = req.getContextPath();
		url = url.replace(contextPath, "").replaceAll("/+", "/");

		for (HandlerMapping handler : handlerMappings) {
			Matcher matcher = handler.getPattern().matcher(url);
			if(!matcher.matches()) {
				continue;
			}
			return handler;
		}
		return null;
	}

	private HandlerAdapter getHandlerAdapter(HandlerMapping handler) {
		if(handlerAdapters.isEmpty()) {
			return null;
		}
		HandlerAdapter ha = handlerAdapters.get(handler);
		if(ha.supports(handler)) {
			return ha;
		}
		return null;
	}

	private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, ModelAndView mv) throws Exception {
		// 调用viewResolver的resolveViewName方法
		if(null == mv) {
			return;
		}
		if(viewResolvers.isEmpty()) {
			return;
		}
		if(viewResolvers != null) {
			for (ViewResolver viewResolver : viewResolvers) {
				View view = viewResolver.resolveViewName(mv.getViewName(), null);
				if(view != null) {
					view.render(mv.getModel(), req, resp);
					return;
				}
			}
		}
	}

	private void initFlashMapManager(ApplicationContext context) {
		// no-op
	}

	private void initRequestToViewNameTranslator(ApplicationContext context) {
		// no-op
	}

	private void initHandlerExceptionResolvers(ApplicationContext context) {
		// no-op
	}

	private void initThemeResolver(ApplicationContext context) {
		// no-op
	}

	private void initLocaleResolver(ApplicationContext context) {
		// no-op
	}

	private void initMultipartResolver(ApplicationContext context) {
		// no-op
	}

}
